#ifndef _BYTE_BUFFER_HPP
#define _BYTE_BUFFER_HPP
#include <cstdint>
#include <sys/types.h>
#include <vector>
#include <sstream>
#include <string.h>

class ByteBufferException : public std::exception
{
public:
    ByteBufferException(std::string exceptStr = ""):_msg(exceptStr){}
    ~ByteBufferException() noexcept = default;
    char const* what() const noexcept override { return _msg.c_str(); }
protected:
    std::string & message() noexcept { return _msg; }
private:
    std::string _msg;
};

class ByteBufferPosException : public ByteBufferException
{
    public:
        ByteBufferPosException(bool add, size_t pos, size_t size, size_t valueSize)
        {
            std::ostringstream ss;
            ss << "Attempted to " << (add ? "put" : "get") << " value with size: "
            << valueSize << " in ByteBuffer (pos: " << pos << " size: " << size
            << ")";

            message().assign(ss.str());
        }
        ~ByteBufferPosException() noexcept = default;
};

class ByteBuffer
{
    public:
        ByteBuffer(uint32_t defaultSize = 1024,uint8_t power = 2)
                    : _maxSize(defaultSize)
                    , _power(power)
                    , _rPos(0)
                    , _wPos(0)
        {
            _storage.clear();
            _storage.resize(_maxSize,0);
        }
        ~ByteBuffer()
        {
            _storage.clear();
            std::vector<uint8_t>().swap(_storage);
            _maxSize = _rPos = _wPos = 0;
        }
        // copy constructor
        ByteBuffer(const ByteBuffer &buf)
                    : _rPos(buf._rPos)
                    , _wPos(buf._wPos)
                    , _storage(buf._storage)
                    , _maxSize(buf._maxSize)
                    , _power(buf._power)
        {
        }
    public:
        inline uint32_t ReadSize()
        {
            return _wPos - _rPos;
        }
        inline uint32_t WriteSize()
        {
            return _maxSize - _wPos;
        }
        inline uint8_t* ReadBegin()
        {
            return &_storage[_rPos];
        }
        inline uint8_t* WriteBegin()
        {
            return &_storage[_wPos];
        }
        inline void ReSize(uint32_t resize)
        {
            _storage.resize(resize,0);
            _maxSize = resize;
            _rPos = _wPos = 0;
        }
        inline uint32_t Size() const
        {
            return _storage.size();
        }
        inline void AddRPos(uint32_t pos)
        {
            _rPos += pos;
            ZeroPosIfEqual();
        }
        inline void AddWPos(uint32_t pos)
        {
            _wPos += pos;
        } 
        inline void Reset()
        {
            _wPos = _rPos = 0;
        }
        inline void Clear()
        {
            _storage.clear();
            _maxSize = _wPos = _rPos = 0;
        }
        inline const uint32_t& WPos()
        {
            return _wPos;
        }
        inline const uint32_t& RPos()
        {
            return _rPos;
        }
    private:
        template<typename T>
        T Read()
        {
            if(ReadSize() < sizeof(T))
            {
                return T();
            }
            T t;
            memcpy(&t,&_storage[_rPos],sizeof(T));
            _rPos += sizeof(T);
            ZeroPosIfEqual();
            return t;
        }
        void Read(uint8_t* data,uint32_t len)
        {
            if(len > ReadSize() || _rPos > _wPos)
            {
                throw ByteBufferPosException(false,_rPos,len,Size());
            }
            memcpy(data,ReadBegin(),len);
            _rPos += len;
            ZeroPosIfEqual();
        }
        template<typename T>
        void Read(T& data)
        {
            Read((uint8_t*)&data,sizeof(T));
        }
        void Write(const uint8_t* data,uint32_t len)
        {
            if(_rPos > _wPos)
            {
                throw ByteBufferPosException(true,_wPos,len,Size());
            }
            Expand(len);
            memcpy(&_storage[_wPos],data,len);
            _wPos += len;
        }
        template<typename T>
        void Write(const T& data)
        {
            Write((const uint8_t*)&data,sizeof(T));
        }
    public:
        ByteBuffer& operator<<(const uint8_t& data)
        {
            Write<uint8_t>(data);
            return *this;
        }
        ByteBuffer& operator<<(const int8_t& data)
        {
            Write<int8_t>(data);
            return *this;
        }
        ByteBuffer& operator<<(const uint16_t& data)
        {
            Write<uint16_t>(data);
            return *this;
        }
        ByteBuffer& operator<<(const int16_t& data)
        {
            Write<int16_t>(data);
            return *this;
        }
        ByteBuffer& operator<<(const uint32_t& data)
        {
            Write<uint32_t>(data);
            return *this;
        }
        ByteBuffer& operator<<(const int32_t& data)
        {
            Write<int32_t>(data);
            return *this;
        }
        ByteBuffer& operator<<(const uint64_t& data)
        {
            Write<uint64_t>(data);
            return *this;
        }
        ByteBuffer& operator<<(const int64_t& data)
        {
            Write<int64_t>(data);
            return *this;
        }
        ByteBuffer& operator<<(const float& data)
        {
            Write<float>(data);
            return *this;
        }
        ByteBuffer& operator<<(const double& data)
        {
            Write<double>(data);
            return *this;
        }
        ByteBuffer& operator<<(const char* str)
        {
            Write((const uint8_t*)str,str ? strlen(str) : 0);
            Write<uint8_t>(0);
            return *this;
        }
        ByteBuffer& operator<<(const std::string& data)
        {
            (*this) << data.c_str();
            return *this;
        }
        void Append(const uint8_t* data,uint32_t dwLen)
        {
            Write(data,dwLen);
        }
    public:
        ByteBuffer& operator>>(uint8_t& data)
        {
            Read<uint8_t>(data);
            return *this;
        }
        ByteBuffer& operator>>(int8_t& data)
        {
            Read<int8_t>(data);
            return *this;
        }
        ByteBuffer& operator>>(uint16_t& data)
        {
            Read<uint16_t>(data);
            return *this;
        }
        ByteBuffer& operator>>(int16_t& data)
        {
            Read<int16_t>(data);
            return *this;
        }
        ByteBuffer& operator>>(uint32_t& data)
        {
            Read<uint32_t>(data);
            return *this;
        }
        ByteBuffer& operator>>(int32_t& data)
        {
            Read<int32_t>(data);
            return *this;
        }    
        ByteBuffer& operator>>(uint64_t& data)
        {
            Read<uint64_t>(data);
            return *this;
        }
        ByteBuffer& operator>>(int64_t& data)
        {
            Read<int64_t>(data);
            return *this;
        } 
        ByteBuffer& operator>>(double& data)
        {
            Read<double>(data);
            return *this;
        }
        ByteBuffer& operator>>(float& data)
        {
            Read<float>(data);
            return *this;
        }    
        ByteBuffer& operator>>(char* data)
        {
            std::string tmp;
            (*this) >> tmp;
            memcpy(data,tmp.c_str(),tmp.size());
            return *this;
        }
        ByteBuffer& operator>>(std::string& data)
        {
            data.clear();
            while(_rPos < _wPos)
            {
                char c = Read<char>();
                if(c == 0)
                {
                    break;
                }
                data += c;
            }
            return *this;
        }
    public:
        ByteBuffer& operator>>(ByteBuffer& buffer)
        {
            uint32_t len = 0;
            Read<uint32_t>(len);
            if(len > 0)
            {
                buffer.ReSize(len);
                Read(buffer.WriteBegin(),len);
            }
            return *this;
        }
        ByteBuffer& operator<<(ByteBuffer& buffer)
        {
            uint32_t len = buffer.ReadSize();
            Write(len);
            if(len > 0)
            {
                Write(buffer.ReadBegin(),len);
            }
            return *this;
        }
    private:
        inline void ZeroPosIfEqual()
        {
            if(_rPos == _wPos)
            {
                _rPos = _wPos = 0;
            }
        }
    public:
        void Expand(uint32_t cnt)
        {
            if(cnt <= WriteSize())
            {
                return ;
            }
            if(_maxSize == 0)
            {
                _maxSize = 1024;
            }
            uint32_t newSize = _maxSize * _power;
            while(uint32_t(newSize - ReadSize()) <= cnt)
            {
                newSize *= _power;
            }
            _maxSize = newSize;
            _storage.resize(_maxSize,0);

            int enReadSize = ReadSize();
            if(enReadSize > 0 && _rPos > 0)
            {
                memcpy(&_storage[0],&_storage[_rPos],enReadSize);
                _wPos = enReadSize;
                _rPos = 0;
            }
        }

    protected:
        std::vector<uint8_t> _storage;
        uint32_t             _rPos;
        uint32_t             _wPos;
        uint8_t              _power;
        uint32_t             _maxSize;             
};

#endif